﻿using System;
using System.Collections.Generic;
using System.Web;
using Jint;
using System.Reflection;
using System.IO;
using System.Net;
using Jint.Native;
using Jint.Delegates;
using System.Configuration;
using System.Web.Configuration;
using System.Text;
using System.Diagnostics;

namespace Htmlx
{
    public class HtmlxHandlerFactory : IHttpHandlerFactory
    {
        public class HtmlxEngine
        {
            JintEngine engine;
            HttpContext context;
            ProxyCodeVisitor proxy;
            public HtmlxEngine(JintEngine engine, HttpContext context)
            {
                this.engine = engine;
                this.context = context;
                proxy = new ProxyCodeVisitor();
                engine.SetFunction("loadAssembly", new Action<string>(LoadAssembly));
                engine.SetFunction("print", new Action<string>(Print));
                engine.SetFunction("debug", new Action(Debug));
                engine.SetFunction("canLoadScript", new Func<JsInstance, bool>(CanLoadScript));
                engine.SetFunction("loadIntoFnsScope", new Action<string>(Load));
                engine.SetFunction("ResolveUrl", new Func<string, string>(ResolveUrl));
                engine.SetFunction("ParseProxyCode", new Func<string, string>(ParseProxyCode));
            }

            public string ParseProxyCode(string script)
            {
                JintEngine.Compile(script, false).Accept(proxy);
                StringBuilder sb = new StringBuilder();
                foreach (Jint.Expressions.FunctionDeclarationStatement function in proxy.PageMethods)
                {
                    sb.Append("function ");
                    sb.Append(function.Name);
                    sb.Append("(callback){");
                    sb.Append("var xhr = new XMLHttpRequest(); xhr.open('GET', '");
                    sb.Append(context.Request.RawUrl);
                    sb.Append('/');
                    sb.Append(function.Name);
                    sb.Append("', true); xhr.onreadystatechange = function(){ if(xhr.readyState==4) { if(callback) callback(xhr.responseText); else eval(xhr.responseText); } }; xhr.send(); }");
                }
                return sb.ToString();
            }

            public static void LoadAssembly(string assemblyName) { Assembly.Load(assemblyName); }
            public static void Print(string line) { System.Diagnostics.Trace.WriteLine(line); }
            public static void Debug() { System.Diagnostics.Debugger.Break(); }
            public bool CanLoadScript(JsInstance instance)
            {
                return (bool)engine.Run(@"
                    var script=arguments[0]; 
                    var runat=script.getAttribute('runat'); 
                    return runat=='both' || runat=='server';
                ");
            }

            public void Load(string fileName)
            {
                using (StreamReader reader = new StreamReader(WebRequest.Create(fileName).GetResponse().GetResponseStream()))
                {
                    engine.Run(reader.ReadToEnd());
                }
            }

            public string ResolveUrl(string urlToResolve)
            {
                if (string.IsNullOrEmpty(urlToResolve))
                    return urlToResolve;
                string resolvedUrl = context.Request.RawUrl.Substring(0, context.Request.RawUrl.Length - context.Request.AppRelativeCurrentExecutionFilePath.Length + 2) + urlToResolve.Substring(2);
                if (resolvedUrl.StartsWith("/\\"))
                    return ResolveUrl("~/" + urlToResolve.Substring(context.Server.MapPath("~/").Length).Replace('\\', '/'));
                return resolvedUrl;
            }

        }

        public HtmlxHandlerFactory()
        {
        }

        #region IHttpHandlerFactory Members

        public IHttpHandler GetHandler(HttpContext context, string requestType, string url, string pathTranslated)
        {
            JintEngine engine = new JintEngine();
            engine.DisableSecurity();
            engine.SetDebugMode(true);
            engine.BreakPoints.Add(new Jint.Debugger.BreakPoint(452, 1));
            engine.Break += new EventHandler<Jint.Debugger.DebugInformation>(engine_Break);
            engine.AllowClr = true;
            new HtmlxEngine(engine, context);
            engine.Run(envjs.env_jint);
            engine.Run(envjs.env1_0);
            engine.Run(envjs.json2);
            engine.SetParameter("Context", context);
            engine.SetParameter("Theme", ((PagesSection)ConfigurationManager.GetSection("system.web/pages")).Theme);
            if (!url.EndsWith(pathTranslated))
            {

            }
            return new HtmlxHandler(engine);
        }

        void engine_Break(object sender, Jint.Debugger.DebugInformation e)
        {
            Debugger.Break();
        }

        public void ReleaseHandler(IHttpHandler handler)
        {
        }

        #endregion
    }
}
